import angular from 'angular';
import template from './ng-image-preview.template.html';

ngImagePreviewDirective.$inject = ['$document', '$compile', '$timeout'];

function ngImagePreviewDirective($document, $compile, $timeout) {
  return {
    restrict: 'A',
    scope: {
      previewSrc: '@?',
      previewTitle: '@?',
      previewAlt: '@?',
      previewDisabled: '=?'
    },
    link: function(scope, element) {
      var body = $document.find('body');
      var overlayScope = null;
      var overlayElement = null;
      var escHandler = null;
      var moveHandler = null;
      var upHandler = null;
      var imageMoveHandler = null;
      var imageUpHandler = null;
      var dragState = {
        active: false,
        startX: 0,
        startY: 0,
        originX: 0,
        originY: 0
      };
      var imageDragState = {
        active: false,
        startX: 0,
        startY: 0,
        originX: 0,
        originY: 0
      };

      element.addClass('ng-image-preview-trigger');
      scope.previewDisabled = !!scope.previewDisabled;

      scope.$watch('previewDisabled', function(value) {
        scope.previewDisabled = !!value;
      });

      function ensureOverlay() {
        if (overlayScope) return;

        overlayScope = scope.$new(true);
        overlayScope.visible = false;
        overlayScope.zoom = 1;
        overlayScope.minZoom = 0.5;
        overlayScope.maxZoom = 4;
        overlayScope.step = 0.2;
        overlayScope.imageSrc = '';
        overlayScope.title = '';
        overlayScope.alt = '';
        overlayScope.position = { x: 0, y: 0 };
        overlayScope.imagePosition = { x: 0, y: 0 };
        overlayScope.imageDragging = false;
        overlayScope.rotation = 0;
        overlayScope.flipX = 1;
        overlayScope.flipY = 1;

        overlayScope.getContainerStyle = function() {
          return {
            '--np-drag-x': overlayScope.position.x + 'px',
            '--np-drag-y': overlayScope.position.y + 'px'
          };
        };

        overlayScope.close = function($event) {
          if ($event) {
            $event.preventDefault();
            $event.stopPropagation();
          }
          closePreview();
        };

        function shouldResetImagePosition() {
          return (
            overlayScope.zoom <= 1 &&
            overlayScope.rotation % 360 === 0 &&
            overlayScope.flipX === 1 &&
            overlayScope.flipY === 1
          );
        }

        overlayScope.zoomIn = function() {
          if (!overlayScope.canZoomIn()) return;
          overlayScope.zoom = Math.min(overlayScope.zoom + overlayScope.step, overlayScope.maxZoom);
          if (shouldResetImagePosition()) {
            overlayScope.imagePosition = { x: 0, y: 0 };
            overlayScope.imageDragging = false;
            imageDragState.active = false;
            detachImageDragListeners();
          }
        };

        overlayScope.zoomOut = function() {
          if (!overlayScope.canZoomOut()) return;
          overlayScope.zoom = Math.max(overlayScope.zoom - overlayScope.step, overlayScope.minZoom);
          if (shouldResetImagePosition()) {
            overlayScope.imagePosition = { x: 0, y: 0 };
            overlayScope.imageDragging = false;
            imageDragState.active = false;
            detachImageDragListeners();
          }
        };

        overlayScope.rotateLeft = function() {
          overlayScope.rotation = (overlayScope.rotation - 90 + 360) % 360;
          overlayScope.imagePosition = { x: 0, y: 0 };
          overlayScope.imageDragging = false;
          imageDragState.active = false;
          detachImageDragListeners();
        };

        overlayScope.rotateRight = function() {
          overlayScope.rotation = (overlayScope.rotation + 90) % 360;
          overlayScope.imagePosition = { x: 0, y: 0 };
          overlayScope.imageDragging = false;
          imageDragState.active = false;
          detachImageDragListeners();
        };

        overlayScope.flipHorizontal = function() {
          overlayScope.flipX = overlayScope.flipX === 1 ? -1 : 1;
          overlayScope.imagePosition = { x: 0, y: 0 };
          overlayScope.imageDragging = false;
          imageDragState.active = false;
          detachImageDragListeners();
        };

        overlayScope.flipVertical = function() {
          overlayScope.flipY = overlayScope.flipY === 1 ? -1 : 1;
          overlayScope.imagePosition = { x: 0, y: 0 };
          overlayScope.imageDragging = false;
          imageDragState.active = false;
          detachImageDragListeners();
        };

        overlayScope.resetView = function() {
          overlayScope.zoom = 1;
          overlayScope.rotation = 0;
          overlayScope.flipX = 1;
          overlayScope.flipY = 1;
          overlayScope.imagePosition = { x: 0, y: 0 };
          overlayScope.imageDragging = false;
          imageDragState.active = false;
          detachImageDragListeners();
        };

        overlayScope.resetZoom = overlayScope.resetView;

        overlayScope.canZoomIn = function() {
          return overlayScope.zoom < overlayScope.maxZoom - 0.01;
        };

        overlayScope.canZoomOut = function() {
          return overlayScope.zoom > overlayScope.minZoom + 0.01;
        };

        overlayScope.canResetView = function() {
          return (
            overlayScope.zoom.toFixed(2) !== '1.00' ||
            overlayScope.rotation % 360 !== 0 ||
            overlayScope.flipX !== 1 ||
            overlayScope.flipY !== 1 ||
            overlayScope.imagePosition.x !== 0 ||
            overlayScope.imagePosition.y !== 0
          );
        };

        overlayScope.getImageStyle = function() {
          return {
            transform: [
              'translate3d(' + overlayScope.imagePosition.x + 'px, ' + overlayScope.imagePosition.y + 'px, 0)',
              'rotate(' + overlayScope.rotation + 'deg)',
              'scale(' + overlayScope.zoom * overlayScope.flipX + ', ' + overlayScope.zoom * overlayScope.flipY + ')'
            ].join(' ')
          };
        };

        overlayScope.isImageDraggable = function() {
          return (
            overlayScope.zoom > 1 ||
            overlayScope.rotation % 360 !== 0 ||
            overlayScope.flipX !== 1 ||
            overlayScope.flipY !== 1
          );
        };

        overlayScope.startImageDrag = function($event) {
          if (!$event) return;
          if (!overlayScope.isImageDraggable()) return;
          if ($event.button !== undefined && $event.button !== 0) return;
          if ($event.which && $event.which !== 1) return;

          $event.preventDefault();
          $event.stopPropagation();

          imageDragState.active = true;
          imageDragState.startX = $event.clientX;
          imageDragState.startY = $event.clientY;
          imageDragState.originX = overlayScope.imagePosition.x;
          imageDragState.originY = overlayScope.imagePosition.y;
          overlayScope.imageDragging = true;

          if (!imageMoveHandler) {
            imageMoveHandler = function(event) {
              if (!imageDragState.active) return;
              event.preventDefault();
              var deltaX = event.clientX - imageDragState.startX;
              var deltaY = event.clientY - imageDragState.startY;
              overlayScope.$applyAsync(function() {
                overlayScope.imagePosition.x = imageDragState.originX + deltaX;
                overlayScope.imagePosition.y = imageDragState.originY + deltaY;
              });
            };
          }

          if (!imageUpHandler) {
            imageUpHandler = function(event) {
              if (!imageDragState.active) return;
              event.preventDefault();
              imageDragState.active = false;
              overlayScope.$applyAsync(function() {
                overlayScope.imageDragging = false;
              });
              detachImageDragListeners();
            };
          }

          attachImageDragListeners();
        };

        overlayScope.startDrag = function($event) {
          if (!$event) return;
          if (dragState.active) return;
          if ($event.button !== undefined && $event.button !== 0) return;
          if ($event.which && $event.which !== 1) return;
          if ($event.target && typeof $event.target.closest === 'function') {
            if (
              $event.target.closest('.ng-image-preview-overlay__close') ||
              $event.target.closest('.ng-image-preview-overlay__toolbar')
            ) {
              return;
            }
          }

          $event.preventDefault();

          dragState.active = true;
          dragState.startX = $event.clientX;
          dragState.startY = $event.clientY;
          dragState.originX = overlayScope.position.x;
          dragState.originY = overlayScope.position.y;

          if (!moveHandler) {
            moveHandler = function(event) {
              if (!dragState.active) return;
              event.preventDefault();
              var deltaX = event.clientX - dragState.startX;
              var deltaY = event.clientY - dragState.startY;
              overlayScope.$applyAsync(function() {
                overlayScope.position.x = dragState.originX + deltaX;
                overlayScope.position.y = dragState.originY + deltaY;
              });
            };
          }

          if (!upHandler) {
            upHandler = function() {
              if (!dragState.active) return;
              dragState.active = false;
              detachDragListeners();
            };
          }

          attachDragListeners();
        };

        overlayElement = $compile(angular.element(template))(overlayScope);
        body.append(overlayElement);
      }

      function attachDragListeners() {
        if (moveHandler) {
          $document.on('mousemove', moveHandler);
        }
        if (upHandler) {
          $document.on('mouseup', upHandler);
        }
      }

      function detachDragListeners() {
        if (moveHandler) {
          $document.off('mousemove', moveHandler);
        }
        if (upHandler) {
          $document.off('mouseup', upHandler);
        }
      }

      function attachImageDragListeners() {
        if (imageMoveHandler) {
          $document.on('mousemove', imageMoveHandler);
        }
        if (imageUpHandler) {
          $document.on('mouseup', imageUpHandler);
        }
      }

      function detachImageDragListeners() {
        if (imageMoveHandler) {
          $document.off('mousemove', imageMoveHandler);
        }
        if (imageUpHandler) {
          $document.off('mouseup', imageUpHandler);
        }
      }

      function openPreview() {
        if (scope.previewDisabled) return;

        ensureOverlay();

        var src = scope.previewSrc || element.attr('data-full-src') || element.attr('src');
        if (!src) return;

        overlayScope.zoom = 1;
        overlayScope.imageSrc = src;
        overlayScope.title = scope.previewTitle || element.attr('title') || '';
        overlayScope.alt = scope.previewAlt || element.attr('alt') || '';
        overlayScope.visible = true;
        overlayScope.position = { x: 0, y: 0 };
        overlayScope.imagePosition = { x: 0, y: 0 };
        overlayScope.rotation = 0;
        overlayScope.flipX = 1;
        overlayScope.flipY = 1;
        overlayScope.imageDragging = false;
        dragState.active = false;
        imageDragState.active = false;
        detachImageDragListeners();

        $timeout(function() {
          body.addClass('ng-image-preview-open');
        }, 0, false);

        attachEscListener();
      }

      function closePreview() {
        if (!overlayScope) return;
        overlayScope.visible = false;
        overlayScope.position = { x: 0, y: 0 };
        overlayScope.imagePosition = { x: 0, y: 0 };
        overlayScope.rotation = 0;
        overlayScope.flipX = 1;
        overlayScope.flipY = 1;
        overlayScope.imageDragging = false;
        body.removeClass('ng-image-preview-open');
        dragState.active = false;
        imageDragState.active = false;
        detachDragListeners();
        detachImageDragListeners();
        detachEscListener();
      }

      function attachEscListener() {
        if (escHandler) return;

        escHandler = function(event) {
          var key = event.key || event.keyCode;
          if (key === 'Escape' || key === 'Esc' || key === 27) {
            scope.$applyAsync(function() {
              closePreview();
            });
          }
        };

        $document.on('keydown', escHandler);
      }

      function detachEscListener() {
        if (!escHandler) return;
        $document.off('keydown', escHandler);
        escHandler = null;
      }

      function handleDblClick(event) {
        event.preventDefault();
        scope.$applyAsync(function() {
          openPreview();
        });
      }

      element.on('dblclick', handleDblClick);

      scope.$on('$destroy', function() {
        element.off('dblclick', handleDblClick);
        detachEscListener();
        detachDragListeners();
        detachImageDragListeners();

        if (overlayElement) {
          overlayElement.remove();
        }

        if (overlayScope) {
          overlayScope.$destroy();
        }

        body.removeClass('ng-image-preview-open');
      });
    }
  };
}

export default ngImagePreviewDirective;
